﻿using System;
using System.Collections.Generic;
using System.Globalization;
using DM4;
using sscore;

namespace DM4Script
{
    public partial class ScriptParse
    {
        private bool HandleDeclare()
        {
            if ((Declare = ParseDeclare()) != null)
                LogAdd("Parsed DECLARE section - OK");
            else
                GetNextToken(); // Пропускаем токен для восстановления после ошибки.
            
            return Declare != null;
        }

        /// <summary>
        /// parses prototype DECLARE: 'name MODULE xxx SESSION yyy PRIORITY 5 [zzzzzzz]'
        /// </summary>
        /// <returns>prototypes declare</returns>
        private Prototypes.DeclareSection ParseDeclare()
        {
            GetNextToken();

            // Считываем список наименований параметров.
            var tasks = new Dictionary<string, Prototypes.ScriptTask>();

            if (_curTok != (char)Token.Task && _curTok != (char)Token.SubTask)
                return ErrorDeclare("Expected TASK or SUBTASK in prototype DECLARE");

            while (true)
            {
                bool hasTask = false;
                if (_curTok == (char)Token.Task || _curTok == (char)Token.SubTask)
                {
                    Prototypes.ScriptTask scriptTask = ParseTask(_curTok == (char)Token.Task, tasks);
                    if (scriptTask == null) return null;

                    tasks.Add(scriptTask.TaskName, scriptTask);
                    hasTask = true;
                }

                if (!hasTask)
                    return ErrorDeclare("Expected TASK, SUBTASK or RUN in prototype declare");

                if (_curTok == (char)Token.Run || _curTok == (int)Token.Eof) break;
            }

            return new Prototypes.DeclareSection(tasks);
        }

        /// <summary>
        /// parses prototype TASK: 'name MODULE xxx SESSION yyy PRIORITY 5 [zzzzzzz]'
        /// </summary>
        /// <returns>prototypes task</returns>
        private Prototypes.ScriptTask ParseTask(bool main, Dictionary<string, Prototypes.ScriptTask> tasks)
        {
            GetNextToken();

            if (_curTok != (char)Token.Identifier)
                return ErrorTask("Expected TASK name in prototype");

            string taskName = _identifierStr.ToUpperInvariant();

            if (ScriptConst.command.Contains(taskName))
                return ErrorTask(String.Format("Name TASK '{0}' is not a valid identifier", taskName));

            if (tasks.ContainsKey(taskName))
                return ErrorTask(String.Format("Doubles name TASK '{0}'", taskName));

            GetNextToken();
            if (_curTok != (char)Token.Module)
                return ErrorTask("Expected word 'MODULE' in prototype");

            GetNextToken();
            if (_curTok != (char)Token.Identifier)
                return ErrorTask("Expected MODULE name in prototype");

            string moduleName = _identifierStr.ToUpperInvariant();

            var param = new Dictionary<string, ParamEntry>();
            var subParam = new Dictionary<string, ParamEntry>();

            //add ModuleCode=код модуля
            subParam.Add(DM4Consts.ModuleCode.ToUpper(), new ParamEntry(moduleName));

            GetNextToken();

            while (_curTok != (char)Token.Run && _curTok != (char)Token.Task && _curTok != (char)Token.SubTask && _curTok != '[' && _curTok != (int)Token.Eof)
            {
                bool hasParam = false;
                string nameParam;
                if (_curTok == (char)Token.SessionCount)
                    nameParam = ScriptConst.SessionsCount;
                else
                    nameParam = "";
                if (_curTok == (char)Token.Priority) nameParam = ScriptConst.Priority;

                if (_curTok == (char)Token.IgnoreErrors) nameParam = ScriptConst.IgnoreErrors;

                if (GetNextToken() != '=')
                {
                    if (nameParam == ScriptConst.IgnoreErrors)
                        param.Add(nameParam.ToUpper(), new ParamEntry(DM4Consts.TRUE, ParamType.Boolean));
                    else
                        return ErrorTask("Expected '=' in prototype");
                }
                else
                {
                    if (!String.IsNullOrEmpty(nameParam))
                    {
                        char nextToken = GetNextToken();

                        if (nameParam == ScriptConst.IgnoreErrors && nextToken == (char)Token.Bool)
                            param.Add(nameParam.ToUpper(), new ParamEntry(_identifierStr, ParamType.Boolean));
                        else
                        {
                            if (nextToken != (char)Token.Number)
                                return ErrorTask("Expected numeric in prototype");
                            if (param.ContainsKey(nameParam))
                                return ErrorTask(String.Format("Doubles parameter {0}", nameParam));
                            param.Add(nameParam.ToUpper(), new ParamEntry(_numVal));
                        }
                        hasParam = true;
                    }

                    if (!hasParam)
                        return ErrorTask("Expected 'parameter' in prototype");

                    GetNextToken();
                }
            }

            if (_curTok == '[')
            {
                GetNextToken();

                while (true)
                {
                    if (_curTok != (char)Token.Identifier)
                        return ErrorTask("Expected 'parameter=value;' in prototype");

                    if (subParam.ContainsKey(_identifierStr.ToUpperInvariant()))
                        return ErrorTask(String.Format("Doubles parameter '{0}'", _identifierStr));

                    string paramSub = _identifierStr.ToUpperInvariant();

                    if (GetNextToken() != '=')
                        return ErrorTask("Expected '=' in prototype");

                    GetNextToken();
                    if (_curTok != (char)Token.Identifier && _curTok != (char)Token.Number)
                        return ErrorTask("Expected 'parameter=value;' in prototype");

                    subParam.Add(paramSub.ToUpper(), _curTok == (char)Token.Identifier ? new ParamEntry(_identifierStr) : new ParamEntry(_numVal));

                    GetNextToken();

                    if (_curTok == ']') break;
                    if (_curTok != ';')
                        return ErrorTask("Expected 'parameter=value;' in prototype");

                    GetNextToken();
                    if (_curTok == ']') break;
                }
                GetNextToken();
            }

            return new Prototypes.ScriptTask(taskName, moduleName, param, subParam, main);
        }
    }
}
